import os
import time
import logging
from datetime import datetime, timedelta
from tqdm import tqdm
import shutil # 用于实际的文件操作 (当前为模拟)
import re
import threading
import sys

# --- 配置项 ---
TASK_FILES_DIR = "tasklist"  # 存放任务配置文件的目录
LOG_FILE_PATH = "backup_scheduler.log"  # 日志文件路径
MAIN_LOOP_INTERVAL_SECONDS =300  # 主循环检查任务的间隔时间 (秒)
SIMULATE_BACKUP = False # True: 模拟备份; False: 尝试实际复制 (请谨慎使用False)
SIMULATE_FILE_COUNT = 50 # 模拟备份时的文件数量
SIMULATE_FILE_DELAY = 0.02 # 模拟备份时每个文件的处理延迟

# --- 日志设置 ---
def setup_logging():
    """配置日志记录器"""
    logging.basicConfig(
        level=logging.DEBUG,
        format="%(asctime)s - %(levelname)s - %(message)s",
        handlers=[
            logging.FileHandler(LOG_FILE_PATH, encoding='utf-8'),
            logging.StreamHandler()  # 同时输出到控制台
        ]
    )

# --- 任务文件处理 ---
def parse_task_file(filepath):
    """
    解析任务配置文件。
    返回一个包含任务配置的字典，如果解析失败则返回 None。
    """
    config = {}
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            for line_number, line in enumerate(f, 1):
                line = line.strip()
                if not line or line.startswith('#'):
                    continue
                if '=' in line:
                    key, value = map(str.strip, line.split('=', 1))
                    config[key] = value
                else:
                    logging.warning(f"文件 {filepath} 第 {line_number} 行格式无效: {line}")
        
        # 提取任务名称，如果 task_name 字段不存在，则从文件名派生
        if 'task_name' not in config:
            config['task_name'] = os.path.splitext(os.path.basename(filepath))[0]
        
        # 基本的参数类型转换和校验
        if 'interval' in config:
            interval_value = config['interval']
            try:
                # 尝试直接转换为整数（分钟数）
                config['interval'] = int(interval_value)
            except ValueError:
                # 如果不是数字，检查是否是预定义的时间单位
                if isinstance(interval_value, str):
                    interval_lower = interval_value.lower().strip()
                    if interval_lower == 'month':
                        config['interval'] = 43200  # 30天 * 24小时 * 60分钟 = 43200分钟
                        logging.info(f"任务 '{config['task_name']}' 的 interval 'month' 已转换为 43200 分钟（30天）。")
                    elif interval_lower == 'week':
                        config['interval'] = 10080  # 7天 * 24小时 * 60分钟 = 10080分钟
                        logging.info(f"任务 '{config['task_name']}' 的 interval 'week' 已转换为 10080 分钟（7天）。")
                    elif interval_lower == 'day':
                        config['interval'] = 1440   # 24小时 * 60分钟 = 1440分钟
                        logging.info(f"任务 '{config['task_name']}' 的 interval 'day' 已转换为 1440 分钟（1天）。")
                    elif interval_lower == 'hour':
                        config['interval'] = 60     # 60分钟
                        logging.info(f"任务 '{config['task_name']}' 的 interval 'hour' 已转换为 60 分钟（1小时）。")
                    else:
                        logging.warning(f"任务 '{config['task_name']}' 的 interval 值无效: {config['interval']}. 支持的值: 数字(分钟)、'month'、'week'、'day'、'hour'。将忽略 interval.")
                        config.pop('interval', None)
                else:
                    logging.warning(f"任务 '{config['task_name']}' 的 interval 值无效: {config['interval']}. 将忽略 interval.")
                    config.pop('interval', None)
        
        if 'fixed_time' in config:
            try:
                hour = int(config['fixed_time'])
                if not (0 <= hour <= 23):
                    raise ValueError("小时必须在0-23之间")
                config['fixed_time'] = hour
            except ValueError as e:
                logging.warning(f"任务 '{config['task_name']}' 的 fixed_time 值无效: {config['fixed_time']} ({e}). 将忽略 fixed_time.")
                config.pop('fixed_time', None)

        if 'backup_count' in config:
            try:
                config['backup_count'] = int(config['backup_count'])
            except ValueError:
                logging.warning(f"任务 '{config['task_name']}' 的 backup_count 值无效: {config['backup_count']}. 默认为 -1 (无限).")
                config['backup_count'] = -1
        else:
            config['backup_count'] = -1 # 默认为无限次

        if 'backup_done_count' in config:
            try:
                config['backup_done_count'] = int(config['backup_done_count'])
            except ValueError:
                config['backup_done_count'] = 0
        else:
            config['backup_done_count'] = 0
            
        if 'last_run_time' not in config or not config['last_run_time']:
            # 如果没有上次运行时间，设置为一个很早的时间，以便立即考虑执行
            config['last_run_time'] = (datetime.now() - timedelta(days=365)).isoformat(sep=' ', timespec='seconds')
            logging.info(f"任务 '{config['task_name']}' 未找到 last_run_time, 设置为一年前。")

        if 'source' not in config or 'destination' not in config:
            logging.error(f"任务 '{config['task_name']}' 缺少 'source' 或 'destination' 配置。")
            return None
            
        return config
    except FileNotFoundError:
        logging.error(f"任务文件未找到: {filepath}")
        return None
    except Exception as e:
        logging.error(f"解析任务文件 {filepath} 失败: {e}", exc_info=True)
        return None

def update_task_file(filepath, updates):
    """
    更新任务配置文件中的特定键值对。
    保留注释和原始文件结构。
    """
    lines = []
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            lines = f.readlines()
    except FileNotFoundError:
        logging.error(f"试图更新任务文件失败，文件未找到: {filepath}")
        return

    new_lines = []
    updated_keys = set(updates.keys())
    keys_in_file = set()

    for line in lines:
        stripped_line = line.strip()
        is_updated_for_this_line = False
        if stripped_line and not stripped_line.startswith('#') and '=' in stripped_line:
            key, _ = map(str.strip, stripped_line.split('=', 1))
            keys_in_file.add(key)
            if key in updates:
                new_lines.append(f"{key} = {updates[key]}\n")
                updated_keys.remove(key) # 标记此键已更新
                is_updated_for_this_line = True
        
        if not is_updated_for_this_line:
            new_lines.append(line)

    # 如果有任何在 updates 中但不在文件中的键 (通常不应该用于此脚本的核心字段)
    # 对于本脚本，我们主要更新已存在的 last_run_time, backup_done_count, backup_count
    # for key_to_add in updated_keys:
    #    if key_to_add not in keys_in_file: # 确保只添加文件中确实没有的
    #        new_lines.append(f"{key_to_add} = {updates[key_to_add]}\n")


    try:
        with open(filepath, 'w', encoding='utf-8') as f:
            f.writelines(new_lines)
        logging.info(f"任务文件已更新: {filepath}")
    except IOError as e:
        logging.error(f"写入任务文件 {filepath} 失败: {e}")


# --- 任务调度 ---
def is_task_due(task_config, current_time):
    """
    检查任务是否已到执行时间。
    task_config: 解析后的任务配置字典。
    current_time: 当前 datetime 对象。
    """
    task_name = task_config.get('task_name', '未命名任务')

    if task_config.get('backup_count') == 0:
        logging.info(f"任务 '{task_name}' backup_count 为 0，已跳过。")
        return False

    try:
        last_run_dt_str = task_config.get('last_run_time', '')
        # 尝试多种ISO格式，因为配置文件中的格式可能是 'YYYY-MM-DD HH:MM:SS'
        try:
            last_run_dt = datetime.fromisoformat(last_run_dt_str)
        except ValueError:
            last_run_dt = datetime.strptime(last_run_dt_str, '%Y-%m-%d %H:%M:%S')

    except ValueError as e:
        logging.warning(f"任务 '{task_name}' 的 last_run_time ('{last_run_dt_str}') 格式无效: {e}。假设任务从未运行。")
        # 设置为一个很早的时间，以便符合执行条件（如果其他条件满足）
        last_run_dt = current_time - timedelta(days=365*10) # 10年前

    # 优先处理 interval，如果没有interval才处理fixed_time
    if 'interval' in task_config and isinstance(task_config['interval'], int):
        interval_minutes = task_config['interval']
        if interval_minutes <= 0:
            logging.warning(f"任务 '{task_name}' 的 interval ({interval_minutes}) 无效，已跳过。")
            return False
        
        # 特殊interval值处理
        if interval_minutes == 43200:  # month = 43200 (30天 * 24小时 * 60分钟)
            # 预定执行时间 = 上次运行时间 + 一个月
            next_run_time = last_run_dt + timedelta(days=30)
            interval_desc = "一个月"
        elif interval_minutes == 1440:  # 1440 = 一天
            next_run_time = last_run_dt + timedelta(days=1)
            interval_desc = "一天"
        elif interval_minutes == 10080:  # 10080 = 7天
            next_run_time = last_run_dt + timedelta(days=7)
            interval_desc = "7天"
        else:
            next_run_time = last_run_dt + timedelta(minutes=interval_minutes)
            interval_desc = f"{interval_minutes} 分钟"
        
        # 检查是否过了预定时间
        if current_time >= next_run_time:
            # 如果过了预定时间，但是上次运行时间不是今天的，立即执行
            last_run_date = last_run_dt.date()
            current_date = current_time.date()
            
            if last_run_date != current_date:
                logging.info(f"任务 '{task_name}' ({interval_desc}) 已过预定时间且上次运行不是今天，立即执行。上次运行: {last_run_dt}, 预定时间: {next_run_time}, 当前: {current_time}")
                print(f"⏰ 任务 '{task_name}' 已过期，立即执行 (间隔: {interval_desc})")
                return True
            else:
                logging.debug(f"任务 '{task_name}' ({interval_desc}) 到期。上次运行: {last_run_dt}, 下次预定: {next_run_time}, 当前: {current_time}")
                print(f"⏰ 任务 '{task_name}' 到期执行 (间隔: {interval_desc})")
                return True
        else:
            remaining_time = next_run_time - current_time
            logging.debug(f"任务 '{task_name}' ({interval_desc}) 未到期。上次运行: {last_run_dt}, 下次预定: {next_run_time}, 当前: {current_time}, 剩余时间: {remaining_time}")
            print(f"⏳ 任务 '{task_name}' 未到期 (间隔: {interval_desc}, 剩余: {remaining_time})")
            return False
    
    # 如果没有interval，则处理 fixed_time
    elif 'fixed_time' in task_config and isinstance(task_config['fixed_time'], int):
        target_hour = task_config['fixed_time']
        # 今天预定的执行时间点
        scheduled_datetime_today = current_time.replace(hour=target_hour, minute=0, second=0, microsecond=0)
        
        if current_time >= scheduled_datetime_today: # 当前时间已达到或超过今天的预定时间
            # 如果上次运行时间早于今天的预定时间点，则任务到期
            if last_run_dt < scheduled_datetime_today:
                logging.debug(f"任务 '{task_name}' (fixed_time: {target_hour}:00) 到期。上次运行: {last_run_dt}, 今天预定: {scheduled_datetime_today}, 当前: {current_time}")
                return True
        # (可选逻辑) 如果错过了昨天的，且现在时间还未到今天的预定时间，是否执行？
        # 当前逻辑是：严格按天和预定时间点执行。如果错过了，就等下一个预定时间点。
        logging.debug(f"任务 '{task_name}' (fixed_time: {target_hour}:00) 未到期。上次运行: {last_run_dt}, 今天预定: {scheduled_datetime_today}, 当前: {current_time}")
        return False

    else:
        logging.warning(f"任务 '{task_name}' 既没有有效的 fixed_time 也没有有效的 interval 配置，无法确定是否到期。")
        return False


# --- 辅助函数 ---
def count_files_in_directory(directory):
    """
    递归计算目录中的文件总数，用于进度条显示
    """
    total_files = 0
    try:
        for root, dirs, files in os.walk(directory):
            total_files += len(files)
    except Exception as e:
        logging.warning(f"计算文件数量时出错: {e}")
    return total_files

def copy_directory_with_progress(src, dst):
    """
    带进度条的目录复制函数
    """
    # 计算总文件数
    print(f"📊 正在计算文件数量...")
    total_files = count_files_in_directory(src)
    print(f"📊 共发现 {total_files} 个文件需要复制")
    
    if total_files == 0:
        print(f"⚠️ 源目录为空或无法访问")
        return
    
    # 创建目标目录
    if not os.path.exists(dst):
        os.makedirs(dst, exist_ok=True)
    
    copied_files = 0
    with tqdm(total=total_files, desc="复制进度", unit="文件", ncols=100, colour='blue') as pbar:
        for root, dirs, files in os.walk(src):
            # 计算相对路径
            rel_path = os.path.relpath(root, src)
            if rel_path == '.':
                dest_dir = dst
            else:
                dest_dir = os.path.join(dst, rel_path)
            
            # 创建目标目录
            if not os.path.exists(dest_dir):
                os.makedirs(dest_dir, exist_ok=True)
            
            # 复制文件
            for file in files:
                src_file = os.path.join(root, file)
                dst_file = os.path.join(dest_dir, file)
                
                try:
                    shutil.copy2(src_file, dst_file)
                    copied_files += 1
                    pbar.set_postfix_str(f"✅ {file}")
                except Exception as e:
                    logging.error(f"复制文件 '{src_file}' 失败: {e}")
                    pbar.set_postfix_str(f"❌ {file}")
                
                pbar.update(1)
    
    print(f"✅ 目录复制完成: 共复制 {copied_files}/{total_files} 个文件")

# --- 任务执行 ---
def execute_task(task_config, task_filepath):
    """
    执行（或模拟执行）备份任务。
    """
    task_name = task_config.get('task_name', os.path.basename(task_filepath))
    logging.info(f"开始执行任务: '{task_name}'")
    print(f"\n🚀 开始执行任务: '{task_name}'")

    source_path = task_config['source']
    destination_template = task_config['destination']
    
    # 检查是否为通配符路径（以*结尾）
    is_wildcard = source_path.endswith('*')
    if is_wildcard:
        # 移除末尾的*，获取实际的源目录路径
        actual_source_path = source_path.rstrip('*').rstrip('/\\').rstrip()
        print(f"🔍 检测到通配符路径，将复制 '{actual_source_path}' 目录下的所有内容")
        logging.info(f"通配符模式: 源路径 '{source_path}' -> 实际路径 '{actual_source_path}'")
    else:
        actual_source_path = source_path

    # 解析目标路径中的日期占位符
    now = datetime.now()
    destination_path = destination_template.replace("%[year]%", str(now.year)) \
                                       .replace("%[month]%", f"{now.month:02d}") \
                                       .replace("%[day]%", f"{now.day:02d}")
    
    logging.info(f"任务 '{task_name}': 源路径 '{source_path}'")
    logging.info(f"任务 '{task_name}': 目标路径 '{destination_path}'")
    if is_wildcard:
        print(f"📁 源路径: {source_path} (通配符模式)")
        print(f"📁 实际源路径: {actual_source_path}")
    else:
        print(f"📁 源路径: {source_path}")
    print(f"📂 目标路径: {destination_path}")

    try:
        # 创建目标目录 (如果不存在)
        if not SIMULATE_BACKUP: # 只有在实际备份时才创建目录
             if not os.path.exists(os.path.dirname(destination_path)): # 确保父目录存在
                os.makedirs(os.path.dirname(destination_path), exist_ok=True)
                logging.info(f"为任务 '{task_name}' 创建了目标父目录: {os.path.dirname(destination_path)}")


        if SIMULATE_BACKUP:
            logging.info(f"任务 '{task_name}': [模拟备份模式]")
            print(f"🔄 模拟备份模式: {task_name}")
            print(f"📊 预计处理文件数量: {SIMULATE_FILE_COUNT}")
            for _ in tqdm(range(SIMULATE_FILE_COUNT), desc=f"模拟备份 '{task_name}'", unit="文件", ncols=100, colour='green'):
                time.sleep(SIMULATE_FILE_DELAY)
            logging.info(f"任务 '{task_name}' 模拟备份完成。")
            print(f"✅ 任务 '{task_name}' 模拟备份完成！")
            simulated_success = True # 模拟总是成功
        else:
            logging.info(f"任务 '{task_name}': [实际备份模式]")
            print(f"🔄 实际备份模式: {task_name}")
            print(f"📋 正在检查源路径...")
            if not os.path.exists(actual_source_path):
                logging.error(f"任务 '{task_name}' 失败: 源路径 '{actual_source_path}' 不存在。")
                print(f"❌ 错误: 源路径不存在 - {actual_source_path}")
                return False
            print(f"✅ 源路径检查通过")
            print(f"🚀 开始复制文件...")

            # 实际文件/目录复制逻辑 (示例)
            # 注意：shutil.copytree 对于已存在的目标文件夹会报错，除非 dirs_exist_ok=True (Python 3.8+)
            # 对于文件，shutil.copy2 会覆盖
            # 这里需要更完善的错误处理和进度反馈（如果可能）
            try:
                if is_wildcard:
                    # 通配符模式：复制目录下的所有内容，但不包含目录本身
                    if not os.path.isdir(actual_source_path):
                        logging.error(f"任务 '{task_name}' 失败: 通配符源路径 '{actual_source_path}' 不是一个目录。")
                        print(f"❌ 错误: 通配符源路径必须是目录 - {actual_source_path}")
                        return False
                    
                    # 确保目标目录存在
                    if not os.path.exists(destination_path):
                        os.makedirs(destination_path, exist_ok=True)
                        print(f"📁 创建目标目录: {destination_path}")
                    
                    print(f"📁 通配符模式: 复制 '{actual_source_path}' 下的所有内容...")
                    
                    # 获取源目录下的所有项目列表
                    items = os.listdir(actual_source_path)
                    total_items = len(items)
                    print(f"📊 共发现 {total_items} 个项目需要复制")
                    
                    # 使用tqdm创建进度条
                    items_copied = 0
                    with tqdm(total=total_items, desc="复制进度", unit="项", ncols=100, colour='green') as pbar:
                        for item in items:
                            source_item = os.path.join(actual_source_path, item)
                            dest_item = os.path.join(destination_path, item)
                            
                            try:
                                if os.path.isdir(source_item):
                                    # 复制子目录
                                    if os.path.exists(dest_item):
                                        print(f"📁 目标目录已存在，将合并: {item}")
                                        shutil.copytree(source_item, dest_item, dirs_exist_ok=True)
                                    else:
                                        shutil.copytree(source_item, dest_item)
                                    pbar.set_postfix_str(f"✅ 目录: {item}")
                                else:
                                    # 复制文件
                                    shutil.copy2(source_item, dest_item)
                                    pbar.set_postfix_str(f"✅ 文件: {item}")
                                items_copied += 1
                            except Exception as item_error:
                                logging.error(f"复制项目 '{item}' 失败: {item_error}")
                                print(f"❌ 复制失败: {item} - {str(item_error)}")
                            pbar.update(1)
                    
                    logging.info(f"通配符模式复制完成: 从 '{actual_source_path}' 复制了 {items_copied}/{total_items} 个项目到 '{destination_path}'")
                    print(f"✅ 通配符复制完成: 共复制 {items_copied}/{total_items} 个项目")
                    
                elif os.path.isdir(actual_source_path):
                    # 普通目录复制模式
                    if os.path.exists(destination_path):
                         logging.warning(f"目标路径 {destination_path} 已存在。对于目录复制，这可能导致问题或覆盖。请检查您的策略。")
                         if os.path.isdir(destination_path): # If destination is an existing directory
                            target_dir_for_copy = os.path.join(destination_path, os.path.basename(actual_source_path.rstrip('/\\')))
                            if os.path.exists(target_dir_for_copy):
                                logging.info(f"目标目录 {target_dir_for_copy} 已存在，将尝试覆盖/合并。")
                            print(f"📁 正在复制目录树...")
                            copy_directory_with_progress(actual_source_path, target_dir_for_copy)
                            logging.info(f"目录 '{actual_source_path}' 已成功备份到 '{target_dir_for_copy}'")
                            print(f"✅ 目录复制成功: {target_dir_for_copy}")
                         else:
                            logging.error(f"任务 '{task_name}' 失败: 目标路径 '{destination_path}' 不是一个有效的目录用于复制目录。")
                            print(f"❌ 错误: 目标路径无效 - {destination_path}")
                            return False
                    else:
                        os.makedirs(os.path.dirname(destination_path.rstrip('/\\')), exist_ok=True)
                        print(f"📁 正在复制目录树...")
                        copy_directory_with_progress(actual_source_path, destination_path)
                        logging.info(f"目录 '{actual_source_path}' 已成功备份到 '{destination_path}'")
                        print(f"✅ 目录复制成功: {destination_path}")

                elif os.path.isfile(actual_source_path):
                    # 文件复制模式
                    dest_file_dir = os.path.dirname(destination_path)
                    if not os.path.exists(dest_file_dir):
                        os.makedirs(dest_file_dir, exist_ok=True)
                    print(f"📄 正在复制文件...")
                    shutil.copy2(actual_source_path, destination_path)
                    logging.info(f"文件 '{actual_source_path}' 已成功备份到 '{destination_path}'")
                    print(f"✅ 文件复制成功: {destination_path}")
                else:
                    logging.error(f"任务 '{task_name}' 失败: 源路径 '{actual_source_path}' 既不是文件也不是目录。")
                    print(f"❌ 错误: 源路径类型无效 - {actual_source_path}")
                    return False
                simulated_success = True # 假设实际操作成功
            except Exception as e:
                logging.error(f"任务 '{task_name}' 实际备份操作失败: {e}", exc_info=True)
                print(f"❌ 备份操作失败: {str(e)}")
                simulated_success = False
                return False # 实际备份失败


        # 更新任务文件状态
        if simulated_success: # 只有成功才更新
            print(f"📝 正在更新任务状态...")
            updates = {
                "last_run_time": now.isoformat(sep=' ', timespec='seconds')
            }
            
            current_done_count = task_config.get('backup_done_count', 0)
            updates["backup_done_count"] = current_done_count + 1

            current_backup_count = task_config.get('backup_count', -1)
            if isinstance(current_backup_count, int) and current_backup_count > 0:
                updates["backup_count"] = current_backup_count - 1
            elif current_backup_count == -1:
                updates["backup_count"] = -1 # 保持无限
            # 如果 current_backup_count 是 0, 它不会被执行到这里 (is_task_due 会返回 False)
            # 但如果逻辑改变，这里可以添加 updates["backup_count"] = 0

            update_task_file(task_filepath, updates)
            logging.info(f"任务 '{task_name}' 执行成功并已更新状态。")
            print(f"🎉 任务 '{task_name}' 执行成功！")
            print(f"📊 已完成备份次数: {current_done_count + 1}")
            if current_backup_count > 0:
                print(f"📋 剩余备份次数: {current_backup_count - 1}")
            elif current_backup_count == -1:
                print(f"📋 备份次数: 无限制")
            print(f"⏰ 最后运行时间: {now.strftime('%Y-%m-%d %H:%M:%S')}")
            return True
        else:
            logging.error(f"任务 '{task_name}' 执行失败。")
            print(f"❌ 任务 '{task_name}' 执行失败！")
            return False

    except Exception as e:
        logging.error(f"执行任务 '{task_name}' 时发生意外错误: {e}", exc_info=True)
        return False

# --- 交互式菜单功能 ---
def show_menu():
    """显示主菜单，支持10秒超时自动选择"""
    print("\n" + "=" * 50)
    print("🔧 备份调度程序 - 主菜单")
    print("=" * 50)
    print("1. 🚀 正常备份 (启动调度程序)")
    print("2. ➕ 新增任务")
    print("3. ⚙️  设置扫描时间")
    print("4. 🚪 退出程序")
    print("=" * 50)
    print("⏰ 10秒内无操作将自动选择 [1] 正常备份")
    
    # 使用线程实现超时输入
    user_input = [None]  # 使用列表来存储结果，便于在线程间共享
    input_received = threading.Event()
    
    def get_input():
        """获取用户输入的线程函数"""
        try:
            user_input[0] = input("请选择操作 (1-4): ").strip()
            input_received.set()
        except EOFError:
            # 处理输入流关闭的情况
            user_input[0] = "1"
            input_received.set()
    
    # 启动输入线程
    input_thread = threading.Thread(target=get_input, daemon=True)
    input_thread.start()
    
    # 等待输入或超时
    if input_received.wait(timeout=10):
        # 用户在10秒内输入了内容
        return user_input[0] if user_input[0] else "1"
    else:
        # 超时，自动选择选项1
        print("\n⏰ 超时！自动选择 [1] 正常备份")
        return "1"

def create_new_task():
    """创建新任务的交互式界面"""
    print("\n📝 创建新任务")
    print("-" * 30)
    
    # 获取任务基本信息
    task_name = input("请输入任务名称: ").strip()
    if not task_name:
        print("❌ 任务名称不能为空！")
        return False
    
    source_path = input("请输入源路径: ").strip()
    if not source_path:
        print("❌ 源路径不能为空！")
        return False
    
    destination_path = input("请输入目标路径: ").strip()
    if not destination_path:
        print("❌ 目标路径不能为空！")
        return False
    
    # 选择备份模式
    print("\n📅 请选择备份模式:")
    print("1. 定时备份 (每天固定时间)")
    print("2. 间隔备份 (按时间间隔)")
    mode_choice = input("请选择模式 (1-2): ").strip()
    
    backup_config = {}
    
    if mode_choice == "1":
        # 定时备份
        while True:
            try:
                fixed_time = input("请输入备份时间 (0-23小时): ").strip()
                fixed_time_int = int(fixed_time)
                if 0 <= fixed_time_int <= 23:
                    backup_config['fixed_time'] = fixed_time
                    break
                else:
                    print("❌ 请输入0-23之间的数字！")
            except ValueError:
                print("❌ 请输入有效的数字！")
    
    elif mode_choice == "2":
        # 间隔备份
        print("\n⏰ 请选择备份间隔:")
        print("1. 每小时 (hour)")
        print("2. 每天 (day)")
        print("3. 每周 (week)")
        print("4. 每月 (month)")
        print("5. 自定义分钟数")
        interval_choice = input("请选择间隔 (1-5): ").strip()
        
        if interval_choice == "1":
            backup_config['interval'] = "hour"
        elif interval_choice == "2":
            backup_config['interval'] = "day"
        elif interval_choice == "3":
            backup_config['interval'] = "week"
        elif interval_choice == "4":
            backup_config['interval'] = "month"
        elif interval_choice == "5":
            while True:
                try:
                    custom_minutes = input("请输入自定义分钟数: ").strip()
                    custom_minutes_int = int(custom_minutes)
                    if custom_minutes_int > 0:
                        backup_config['interval'] = custom_minutes
                        break
                    else:
                        print("❌ 请输入大于0的数字！")
                except ValueError:
                    print("❌ 请输入有效的数字！")
        else:
            print("❌ 无效选择！")
            return False
    else:
        print("❌ 无效选择！")
        return False
    
    # 备份次数设置
    while True:
        backup_count_input = input("请输入备份次数 (-1表示无限制): ").strip()
        try:
            backup_count = int(backup_count_input)
            if backup_count >= -1:
                backup_config['backup_count'] = backup_count_input
                break
            else:
                print("❌ 请输入-1或大于等于0的数字！")
        except ValueError:
            print("❌ 请输入有效的数字！")
    
    # 生成任务文件
    safe_task_name = re.sub(r'[<>:"/\\|?*]', '_', task_name)
    task_filename = f"{safe_task_name}.txt"
    task_filepath = os.path.join(TASK_FILES_DIR, task_filename)
    
    # 检查文件是否已存在
    if os.path.exists(task_filepath):
        overwrite = input(f"⚠️  任务文件 '{task_filename}' 已存在，是否覆盖？ (y/n): ").strip().lower()
        if overwrite != 'y':
            print("❌ 任务创建已取消！")
            return False
    
    # 创建任务配置内容
    task_content = f"""# 任务配置文件 - {task_name}
task_name = {task_name}
source = {source_path}
destination = {destination_path}
"""
    
    # 添加备份模式配置
    for key, value in backup_config.items():
        task_content += f"{key} = {value}\n"
    
    # 添加自动管理字段
    task_content += """# 以下字段由程序自动管理
last_run_time = 2023-01-01 00:00:00
backup_done_count = 0
"""
    
    # 确保任务目录存在
    if not os.path.exists(TASK_FILES_DIR):
        os.makedirs(TASK_FILES_DIR)
    
    # 写入任务文件
    try:
        with open(task_filepath, 'w', encoding='utf-8') as f:
            f.write(task_content)
        print(f"✅ 任务 '{task_name}' 创建成功！")
        print(f"📁 任务文件: {task_filepath}")
        return True
    except Exception as e:
        print(f"❌ 创建任务文件失败: {e}")
        return False

def set_scan_interval():
    """设置扫描时间间隔"""
    global MAIN_LOOP_INTERVAL_SECONDS
    
    print(f"\n⏰ 当前扫描间隔: {MAIN_LOOP_INTERVAL_SECONDS} 秒")
    print("-" * 30)
    
    while True:
        try:
            new_interval = input("请输入新的扫描间隔 (秒): ").strip()
            new_interval_int = int(new_interval)
            if new_interval_int > 0:
                MAIN_LOOP_INTERVAL_SECONDS = new_interval_int
                print(f"✅ 扫描间隔已设置为 {MAIN_LOOP_INTERVAL_SECONDS} 秒")
                return True
            else:
                print("❌ 请输入大于0的数字！")
        except ValueError:
            print("❌ 请输入有效的数字！")

def wait_with_interrupt(seconds):
    """等待指定秒数，但允许用户按回车键中断等待"""
    print(f"⏰ 将在 {seconds} 秒后再次扫描...")
    print("💡 提示: 按回车键可立即返回主菜单")
    
    # 使用线程来实现可中断的等待
    interrupt_event = threading.Event()
    
    def input_thread():
        try:
            input()  # 等待用户按回车
            interrupt_event.set()
        except EOFError:
            pass
    
    # 启动输入线程
    input_thread_obj = threading.Thread(target=input_thread, daemon=True)
    input_thread_obj.start()
    
    # 等待指定时间或用户中断
    if interrupt_event.wait(timeout=seconds):
        return True  # 用户中断
    return False  # 超时

def run_backup_scheduler():
    """运行备份调度程序的主循环"""
    print("\n🚀 启动备份调度程序...")
    print(f"⏰ 扫描间隔: {MAIN_LOOP_INTERVAL_SECONDS} 秒")
    print("按 Ctrl+C 或在等待期间按回车键可以返回主菜单\n")
    
    # 确保任务目录存在
    if not os.path.exists(TASK_FILES_DIR):
        os.makedirs(TASK_FILES_DIR)
        logging.info(f"任务目录 '{TASK_FILES_DIR}' 已创建。")
        # 创建一个示例任务文件
        sample_task_content = """# 这是一个示例任务的配置文件 (可以安全删除或修改)
task_name = 每日示例备份
source = C:\\Users\\YourUser\\Documents\\ImportantStuff_Sample
destination = D:\\Backups\\Sample\\%[year]%-%[month]%-%[day]%
# 每晚 23 点执行
fixed_time = 23
# 无限次备份
backup_count = -1
# 以下由脚本自动管理
last_run_time = 2023-01-01 00:00:00
backup_done_count = 0
"""
        sample_task_path = os.path.join(TASK_FILES_DIR, "example_daily_backup.txt")
        try:
            with open(sample_task_path, 'w', encoding='utf-8') as f:
                f.write(sample_task_content)
            logging.info(f"已在 '{TASK_FILES_DIR}' 中创建示例任务文件: {sample_task_path}")
            logging.info("请修改示例任务文件中的 source 和 destination 路径，或创建您自己的任务文件。")
        except IOError as e:
            logging.error(f"创建示例任务文件失败: {e}")

    # 主循环
    try:
        while True:
            current_scan_time = datetime.now()
            logging.info(f"[{current_scan_time.strftime('%Y-%m-%d %H:%M:%S')}] 开始扫描任务目录: '{TASK_FILES_DIR}'")
            print(f"\n🔍 [{current_scan_time.strftime('%Y-%m-%d %H:%M:%S')}] 开始扫描任务目录: '{TASK_FILES_DIR}'")
            
            found_tasks = False
            executed_tasks = 0
            try:
                task_files = [f for f in os.listdir(TASK_FILES_DIR) if f.lower().endswith(".txt")]
                if task_files:
                    print(f"📋 发现 {len(task_files)} 个任务文件")
                
                for filename in task_files:
                    found_tasks = True
                    filepath = os.path.join(TASK_FILES_DIR, filename)
                    logging.debug(f"正在处理任务文件: {filepath}")
                    print(f"\n📄 正在检查任务文件: {filename}")
                    
                    task_config = parse_task_file(filepath)
                    if not task_config:
                        logging.warning(f"无法解析任务文件，已跳过: {filepath}")
                        print(f"⚠️  无法解析任务文件，已跳过: {filename}")
                        continue

                    task_name_log = task_config.get('task_name', filepath)
                    print(f"📝 任务名称: {task_name_log}")
                    
                    # 检查任务是否到期
                    if is_task_due(task_config, current_scan_time):
                        logging.info(f"任务 '{task_name_log}' 已到期，准备执行。")
                        if execute_task(task_config, filepath):
                            executed_tasks += 1
                    # else 的情况已经在 is_task_due 函数中打印了状态
                
                if not found_tasks:
                    logging.info(f"在 '{TASK_FILES_DIR}' 中未找到任何 .txt 任务文件。")
                    print(f"📭 在 '{TASK_FILES_DIR}' 中未找到任何 .txt 任务文件。")

            except Exception as e:
                logging.error(f"处理任务时发生主循环错误: {e}", exc_info=True)
                print(f"❌ 处理任务时发生错误: {str(e)}")

            logging.info(f"本轮任务扫描结束。将在 {MAIN_LOOP_INTERVAL_SECONDS} 秒后再次扫描。")
            print(f"\n📊 本轮扫描结果: 检查了 {len(task_files) if found_tasks else 0} 个任务，执行了 {executed_tasks} 个任务")
            print("=" * 60)
            
            # 使用可中断的等待
            if wait_with_interrupt(MAIN_LOOP_INTERVAL_SECONDS):
                print("\n🔙 正在返回主菜单...")
                logging.info("用户选择返回主菜单")
                break
                
    except KeyboardInterrupt:
        print("\n\n🔙 检测到 Ctrl+C，返回主菜单...")
        logging.info("用户通过 Ctrl+C 返回主菜单")

# --- 主程序 ---
def main():
    """主程序入口"""
    setup_logging()
    logging.info("备份调度程序已启动。")
    
    while True:
        try:
            choice = show_menu()
            
            if choice == "1":
                # 正常备份模式
                run_backup_scheduler()
                
            elif choice == "2":
                # 新增任务
                create_new_task()
                input("\n按回车键返回主菜单...")
                
            elif choice == "3":
                # 设置扫描时间
                set_scan_interval()
                input("\n按回车键返回主菜单...")
                
            elif choice == "4":
                # 退出程序
                print("\n👋 感谢使用备份调度程序！")
                logging.info("用户选择退出程序。")
                break
                
            else:
                print("\n❌ 无效选择，请输入1-4之间的数字！")
                input("按回车键继续...")
                
        except KeyboardInterrupt:
            print("\n\n⚠️  检测到 Ctrl+C，返回主菜单...")
            continue
        except Exception as e:
            print(f"\n❌ 发生错误: {e}")
            logging.error(f"主菜单发生错误: {e}", exc_info=True)
            input("按回车键继续...")



if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        logging.info("备份调度程序被用户中断。正在关闭...")
    except Exception as e:
        logging.critical(f"备份调度程序发生致命错误并退出: {e}", exc_info=True)
    finally:
        logging.info("备份调度程序已关闭。")
